package com.sjzd.entity;

import com.sjzd.ConfigParser;
import com.sjzd.exception.NullObjectException;
import com.sjzd.menu.GameMenu;
import com.sjzd.message.BulletStatusMessage;
import com.sjzd.message.StatusMessageBase;
import com.sjzd.message.TankStatusMessage;
import com.sjzd.net.ClientNet;

import java.awt.*;
import java.util.UUID;

/**
 *  坦克实体
 */
public class Tank extends AbstractBase implements Publisher {
    // id
    private String id;
    // 昵称
    private String nickname;
    // 血量
    private Blood blood;
    // 炮筒
    private Gun gun;
    // 原来的坐标
    private int oldX;
    private int oldY;

    private ClientNet clientNet;
    private TankStatusMessage tankStatusMessage;

    public Tank(GameMenu gameMenu, String id, String nickname, int X, int Y, int speed) {
        super(gameMenu, X, Y, speed);
        this.id = id;
        this.nickname = nickname;
        this.clientNet = gameMenu.getClientNet();
        blood = new Blood(X, Y -  configParser.getBLOOD_HEIGHT(),
                configParser.getTANK_SPEED());
        gun = new Gun(X + configParser.getTANK_WIDTH() / 2,
                Y + configParser.getTANK_HEIGHT() / 2,
                configParser.getTANK_SPEED());
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public Blood getBlood() {
        return blood;
    }

    public void setBlood(Blood blood) {
        this.blood = blood;
    }

    public Gun getGun() {
        return gun;
    }

    public void setGun(Gun gun) {
        this.gun = gun;
    }

    public TankStatusMessage getTankStatusMessage() {
        return tankStatusMessage;
    }

    public void setTankStatusMessage(TankStatusMessage tankStatusMessage) {
        this.tankStatusMessage = tankStatusMessage;
    }

    @Override
    public void draw(AbstractBase abstractBase, ConfigParser configParser, Graphics g) {
        move();
        if (checkOverBoundary() || checkHitWall()) {
            recover();
        }
        if (checkHitTank()) {
            isExist = false;
            // 发送给其他用户坦克毁灭信息
            tankStatusMessage.update(X, Y, sinX, sinY, isExist);
            publishMessage(tankStatusMessage);
            return;
        }
        /* for test **/
//        System.out.println("坦克所在位置： X:" + getX() + "  Y:" + Y);
//        System.out.println("坦克目的地位置： targetX:" + getTargetX() + "  targetY:" + getTarY);
        // 坦克主体
        g.setColor(Color.BLUE);
        g.fillOval(X, Y,
                configParser.getTANK_WIDTH(), configParser.getTANK_HEIGHT());
        blood.X = X;
        blood.Y = Y -  configParser.getBLOOD_HEIGHT();
        gun.X = X + configParser.getTANK_WIDTH() / 2;
        gun.Y = Y + configParser.getTANK_HEIGHT() / 2;
        blood.draw(this, configParser, g);
        gun.draw(this, configParser, g);
        g.drawString(nickname, X + 20, blood.Y - 10);
    }

    @Override
    public Rectangle getRectangle() {
        rectangle.setBounds(X, Y, configParser.getTANK_WIDTH(), configParser.getTANK_HEIGHT());
        return rectangle;
    }

    @Override
    public void move() {
        if (targetX == -1 || targetY == -1) {
            return;
        }
        oldX = X;
        oldY = Y;
        // 允许与目标坐标存在10的距离偏差
        int centerX = X + configParser.getTANK_WIDTH() / 2;
        int centerY = Y + configParser.getTANK_HEIGHT() / 2;
        int internalX = centerX - targetX;
        int internalY = centerY - targetY;
        if (internalX <= -10 || internalX >= 10) {
            X += Math.round(speed * sinX);
        }
        if (internalY <= -10 || internalY >= 10) {
            Y += Math.round(speed * sinY);
        }
    }

    /**
     *  当检测到坦克撞到墙或越界后，调用此方法返回到移动前状态
     */
    private void recover() {
        X = oldX;
        Y = oldY;
    }

    @Override
    public void changeTarget(int targetX, int targetY) {
        this.targetX = targetX;
        this.targetY = targetY;
        int centerX = X + configParser.getTANK_WIDTH() / 2;
        int centerY = Y + configParser.getTANK_HEIGHT() / 2;
        sinX = calculateSin(centerX, centerY, targetX, targetY);
        sinY = calculateSin(centerY, centerX, targetY, targetX);
        // 发送位置改变信息
        tankStatusMessage.update(X, Y, sinX, sinY, isExist);
        publishMessage(tankStatusMessage);
    }

    /**
     *  检测坦克是否越界
     *  true : 越界
     *  false : 没越界
     * @return
     */
    private boolean checkOverBoundary() {
        if (X < 0 || Y < 0 ||
                X + configParser.getTANK_WIDTH() > configParser.getWINDOW_WIDTH() ||
                Y + configParser.getTANK_HEIGHT() > configParser.getWINDOW_HEIGHT()) {
            return true;
        }
        return false;
    }

    /**
     *  是否撞墙
     * @return
     */
    private boolean checkHitWall() {
        return false;
    }

    /**
     *  是否碰撞其他坦克
     * @return
     */
    private boolean checkHitTank() {
        for (Tank tank : gameMenu.getOtherTankList()) {
            if (getRectangle().intersects(tank.getRectangle())) {
                return true;
            }
        }
        return false;
    }

    /**
     *  发射炮弹
     */
    public void fire() throws NullObjectException {
        int startX = X + configParser.getTANK_WIDTH() / 2 +
                (int)Math.round(configParser.getGUN_LENGTH() * sinX) - configParser.getBULLET_WIDTH() / 2;
        int startY = Y + configParser.getTANK_HEIGHT() / 2 +
                (int)Math.round(configParser.getGUN_LENGTH() * sinY) - configParser.getBULLET_HEIGHT() / 2;
        Bullet bullet = new Bullet(gameMenu, UUID.randomUUID().toString(),
                 id, startX, startY, sinX, sinY, configParser.getBULLET_SPEED());
        bullet.setBulletStatusMessage(new BulletStatusMessage(bullet.getId(), bullet.X,
                bullet.Y, bullet.sinX, bullet.sinY, bullet.isExist));
        publishMessage(bullet.getBulletStatusMessage());
        gameMenu.addBullet(bullet);
    }

    @Override
    public void publishMessage(StatusMessageBase statusMessageBase) {
        clientNet.sendMsg(statusMessageBase);
    }
}
